#ifndef EDGELINKEDLIST_H_
#define EDGELINKEDLIST_H_

#include <iostream>
#include <string>
#include "Node.h"
using namespace std;

class EdgeLinkedList {

private:
	class EdgeInGraph {

	private:
		const Node& m_out; // reference to out-node
		const Node& m_in; // reference to in-node

	public:

		/***************************************************************************************
		 * function name: EdgeInGraph(const Node& nodeOut, const Node& nodeIn)
		 * The Input: Two references to nodes to be linked by new edge
		 * The output: None
		 * The Function operation: Creates a new Edge object which links between two given nodes
		 ***************************************************************************************/
		EdgeInGraph(const Node& nodeOut, const Node& nodeIn);

		/***************************************************************************************
		 * function name: EdgeInGraph(const EdgeInGraph& edgeToCopy)
		 * The Input: Edge to be copied
		 * The output: None
		 * The Function operation: Creates a new Edge object which links between the nodes that
		 * 						   the given edge links
		 ***************************************************************************************/
		EdgeInGraph(const EdgeInGraph& edgeToCopy);

		/***************************************************************************************
		 * function name: ~EdgeInGraph()
		 * The Input: None
		 * The output: None
		 * The Function operation: Destroys Edge object
		 ***************************************************************************************/
		~EdgeInGraph();

		/***************************************************************************************
		 * function name: getNodeOut()
		 * The Input: None
		 * The output: Reference to node origin of edge
		 * The Function operation: Returns out node reference attribute. Method does not change
		 * 					       Edge object.
		 ***************************************************************************************/
		const Node& getNodeOut() const {return m_out;}

		/***************************************************************************************
		 * function name: getNodeIn()
		 * The Input: None
		 * The output: Reference to node destination of edge
		 * The Function operation: Returns in node reference attribute. Method does not change
		 * 					       Edge object.
		 ***************************************************************************************/
		const Node& getNodeIn() const {return m_in;}

		/***************************************************************************************
		 * function name: == (const EdgeInGraph& operand) const
		 * The Input: Edge operand to compare with
		 * The output: True if given operand is equal to object
		 * The Function operation: Compares edge's node attributes. Operator does not
		 * 						   change the object
		 ***************************************************************************************/
		bool operator == (const EdgeInGraph& operand) const;

		/***************************************************************************************
		 * function name: != (const EdgeInGraph& operand) const
		 * The Input: Edge operand to compare with
		 * The output: True if given operand is not equal to object
		 * The Function operation: Compares edge's node attributes. Operator does not
		 * 						   change the object
		 ***************************************************************************************/
		bool operator != (const EdgeInGraph& operand) const;

		/***************************************************************************************
		 * function name: < (const EdgeInGraph& operand) const
		 * The Input: Edge operand to compare with
		 * The output: True if given operand's out and in nodes' values are bigger than object's
		 * The Function operation: Compares edge's node attributes. Operator does not
		 * 						   change the object
		 ***************************************************************************************/
		bool operator < (const EdgeInGraph& operand) const;

		/***************************************************************************************
		 * function name: <= (const EdgeInGraph& operand) const
		 * The Input: Edge operand to compare with
		 * The output: True if both edges' out node value is equal and  given operand's in node
		 * 			   value is bigger
		 * The Function operation: Compares edge's node attributes. Operator does not
		 * 						   change the object
		 ***************************************************************************************/
		bool operator <= (const EdgeInGraph& operand) const;

		/***************************************************************************************
		 * function name: toString() const
		 * The Input: None
		 * The output: The string: "(out_Node_Value->in_Node_Value)"
		 * The Function operation: Converts edge to a string
		 ***************************************************************************************/
		 const string toString() const;

		/***************************************************************************************
		 * function name: << (ostream &os, const Edge& edgeToPrint)
		 * The Input: Reference to Edge to be printed
		 * The output: ostream reference
		 * The Function operation: TODO
		 ***************************************************************************************/
		friend ostream& operator << (ostream &os, const EdgeInGraph& edgeToPrint);
	};


	class ListNode {

		private:

		public:
			EdgeInGraph& m_edgeData; // reference to edge data
			ListNode* m_prevListNode; // pointer to previous node on list
			ListNode* m_nextListNode; // pointer to next node on list

			/***************************************************************************************
			 * function name: Node (EdgeInGraph& edgeData)
			 * The Input: Edge data to be set as data attribute
			 * The output: None
			 * The Function operation: Creates a new node on list pointing to given edge
			 ***************************************************************************************/
			ListNode(EdgeInGraph& edgeData);

			/***************************************************************************************
			 * function name: Node(const ListNode& listNodeToCopy)
			 * The Input: List node to be copied
			 * The output: None
			 * The Function operation: Creates a new list node identical to given list node
			 ***************************************************************************************/
			ListNode(const ListNode& listNodeToCopy);

			/****************************************************************************************
			 * function name: ~ListNode()
			 * The Input: None
			 * The output: None
			 * The Function operation: Destructs list node object
			 ***************************************************************************************/
			~ListNode();
	}; // end ListNode class

	ListNode* m_headDummy; // pointer to first node on list
	ListNode* m_tailDummy; // pointer to last node on list
	ListNode* m_currNode; // pointer to current node on list, user can change the node it is pointing at and get a pointer to current data // TODO DEBUG!!!

	/***************************************************************************************
	 * function name: findPlace(const Edge& newEdge)
	 * The Input: Reference to edge to add
	 * The output: List node pointer to which the new edge should be linked.
	 * 			   If an edge with new edge's graph nodes already exists, NULL is returned
	 * The Function operation: The method checks if an edge with the new edge's graph nodes
	 * 						   already exists. If no such edge was found, the method
	 * 						   returns a list node pointer which before it new edge will be linked
	 ***************************************************************************************/
	ListNode* findPlace(const EdgeInGraph& newEdge) const; // TODO DEBUG!!


	/***************************************************************************************
	 * function name: internalAdd(Edge& edgeToAdd, ListNode* before)
	 * The Input: Reference to edge to be added and a pointer to the list node where the
	 * 			  new edge will be linked
	 * The output: None
	 * The Function operation: The method creates a new list node pointing to the new edge
	 * 					       and links the new edge in the given location to the list
	 ***************************************************************************************/
	void internalAdd(EdgeInGraph& edgeToAdd, ListNode* before); // TODO DEBUG!!


	public:
	/***************************************************************************************
	 * function name: EdgeLinkedList()
	 * The Input: None
	 * The output: None
	 * The Function operation: Creates a new edge linked list
	 ***************************************************************************************/
	EdgeLinkedList();

	/***************************************************************************************
	 * function name: EdgeLinkedList(const EdgeLinkedList& listToCopy)
	 * The Input: List to be copied
	 * The output: None
	 * The Function operation: Creates a new list identical to given list
	 ***************************************************************************************/
	EdgeLinkedList(const EdgeLinkedList& listToCopy);

	/***************************************************************************************
	 * function name: ~EdgeLinkedList()
	 * The Input: None
	 * The output: None
	 * The Function operation: Deletes the edge linked list but not the pointed data
	 ***************************************************************************************/
	~EdgeLinkedList();

	/***************************************************************************************
	 * function name: addNode(const Edge& newEdge)
	 * The Input: Reference to edge to be added
	 * The output: True if edge was added, False is not
	 * The Function operation: If there is already an edge with the new edge's graph nodes,
	 * 						   edge is not added.
	 * 						   Otherwise, it adds the edge and keeps the list sorted- main
	 * 						   sorting by out-nodes' numbers, secondary sorting by in-nodes'
	 * 						   numbers; from lowest number value to highest
	 ***************************************************************************************/
	//bool addNode(const EdgeInGraph& newEdge);
	bool addNode(const Node& outNode, const Node& inNode);

	/***************************************************************************************
	 * function name: removeListNode(const EdgeInGraph& edgeDataToRmv)
	 * The Input: Reference to edge data whose list node should be removed
	 * The output: True if removal was successful, False if not
	 * The Function operation: Search for list node that hold given data. If one was found,
	 * 						   it is removed
	 ***************************************************************************************/
	bool removeListNode(const EdgeInGraph& edgeDataToRmv);

	/***************************************************************************************
	 * function name: << (ostream &os, const Edge& edgeToPrint)
	 * The Input: Reference to Edge to be printed
	 * The output: ostream reference
	 * The Function operation: TODO
	 ***************************************************************************************/
	friend ostream& operator << (ostream &os, const EdgeInGraph& edgeToPrint);

	/***************************************************************************************
	 * function name: operator << (ostream &os, const EdgeLinkedList& listToPrint)
	 * The Input: Reference to edge liked list to be printed
	 * The output: ostream reference
	 * The Function operation: TODO
	 ***************************************************************************************/
	friend ostream& operator << (ostream &os, const EdgeLinkedList& listToPrint);

	/***************************************************************************************
	 * function name: searchByKey(const EdgeInGraph& key)
	 * The Input: Edge (in graph) to look for
	 * The output: Pointer to edge that's equal to given key. If no such edge exists,
	 * 			   NULL is returned.
	 * The Function operation: Scans list- if a match is found, a pointer to this edge is
	 * 						   returned. If not, NULL is returned.
	 * 						   Method does not change list object
	 ***************************************************************************************/
	//const EdgeInGraph* searchByKey(const EdgeInGraph& key) const; // TODO DEBUG!!!
	bool searchByKey(const EdgeInGraph& key) const;

	/***************************************************************************************
	 * function name: searchByKey(const Node& outNode, const& Node inNode)
	 * The Input: Out and in nodes of edge to look for
	 * The output: Pointer to edge whose out and in node are equal to given keys. If no such
	 * 			   edge exists, NULL is returned.
	 * The Function operation: Scans list- if a match is found, a pointer to this edge is
	 * 						   returned. If not, NULL is returned.
	 * 						   Method does not change list object
	 ***************************************************************************************/
	//const EdgeInGraph* searchByKey(const Node& outNode, const Node inNode) const; // TODO DEBUG!!!
	bool searchByKey(const Node& outNode, const Node& inNode) const;

	/***************************************************************************************
	 * function name: removeEdges(const Node& nodeToRmv)
	 * The Input: Reference to graph node that edges that link it should be removed
	 * The output: None
	 * The Function operation: Removes all edges that have given graph node as out/in node
	 ***************************************************************************************/
	void removeEdges(const Node& nodeToRmv);

	/***************************************************************************************
	 * function name: initializeCurrNode()
	 * The Input: None
	 * The output: None
	 * The Function operation: Sets current node pointer to point at list's head dummy node
	 ***************************************************************************************/
	void initializeCurrNode() {m_currNode = m_headDummy;}

	/***************************************************************************************
	 * function name: incrmntCurrNode()
	 * The Input: None
	 * The output: False if reached list's tail dummy node or if a next list node does
	 * 			   not exist. Otherwise, returns true
	 * The Function operation: Sets current node pointer to point at list's head dummy node
	 ***************************************************************************************/
	bool incrmntCurrNode();

	/***************************************************************************************
	 * function name: getCurrEdge()
	 * The Input: None
	 * The output: Reference to edge data of list node being pointed by current node pointer
	 * The Function operation: Returns edge data attribute of list node being pointed by
	 * 						   current node pointer. Method does not change the object
	 ***************************************************************************************/
	const EdgeInGraph& getCurrEdge() const {return m_currNode->m_edgeData;}

	/***************************************************************************************
	 * function name: operator == (const EdgeLinkedList& listOperand)
	 * The Input: Edge linked list to compare with
	 * The output: True if all edges are equal, False if not
	 * The Function operation: Compares each list's nodes. Method does not change the object
	 ***************************************************************************************/
	bool operator == (const EdgeLinkedList& listOperand) const;

};

#endif
